package com.lottery.game.runner;

import com.alibaba.fastjson.JSONObject;
import com.lottery.game.common.constants.GameConstants;
import com.lottery.game.common.utils.DateUtils;
import com.lottery.game.core.model.ResultEntity;
import com.lottery.game.core.modules.entity.game.GameRoom;
import com.lottery.game.core.modules.entity.user.GameIssue;
import com.lottery.game.core.modules.service.app.BetService;
import com.lottery.game.core.utils.BetUtils;
import com.lottery.game.core.utils.ResultCode;
import com.lottery.game.runner.v2.utils.DataUtil;
import com.lottery.game.websocket.RobotSpeak;


import org.springframework.core.task.TaskExecutor;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import lombok.extern.slf4j.Slf4j;

/**
 * RoomCoreExecutor 替代RoomManger
 * @author Administrator
 *
 */
@Slf4j
@Deprecated
public class RoomManager extends Thread {

	private TaskExecutor taskExecutor;
	private RobotSpeak robotSpeak;
	private BetService betService;
	private GameRoom roomType;

	// 是否是手动控制启动的，如果是手动控制启动，前面参数初始化有些不一致
	private boolean isManual;
	private String initExpect;
	private Integer initRemainTime;

	// 标志线程
	private volatile Thread blinker;

	public RoomManager(TaskExecutor taskExecutor, RobotSpeak robotSpeak, BetService betService, GameRoom roomType,
			boolean isManual, String initExpect, Integer initRemainTime) {
		super();
		this.taskExecutor = taskExecutor;
		this.robotSpeak = robotSpeak;
		this.betService = betService;
		this.roomType = roomType;
		this.isManual = isManual;
		this.initExpect = initExpect;
		this.initRemainTime = initRemainTime;
	}

	/**
	 * 用户手动停止线程
	 */
	public void userStop() {
		System.out.println("stop is do");
		blinker = null;
	}

	@Override
	public void run() {
		// bean参数构建，描述房间运行状态等
		RoomBean bean = new RoomBean();
		bean.setBetType(roomType.getRoomLotteryTicketType());
		bean.setGameTime(roomType.getGameTime());
		bean.setDuration(roomType.getDuration());
		bean.setBetService(betService);
		GameRoom roomTime = betService.queryDownTimeByType(roomType.getRoomLotteryTicketType());

		// 如果是手动进行
		if (isManual) {
			// 用户手动输入005期 210秒，则210秒后005期封盘 这里设置期号为004期 开奖时间为004期理论的开奖时间即
			// 当前之间+210秒+提前封盘时间-游戏时间
			// 这里设置为用户设置的初始期的前一期，因为下面会加回来 获取下一期的。
			bean.setExpect(BetUtils.getEarlyExpect(initExpect));
			int changeSecond = initRemainTime + roomType.getDuration() - bean.getGameTime();
			// 开奖时间为004期理论的开奖时间即 当前之间+210秒+提前封盘时间-游戏时间
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(new Date());
			calendar.add(Calendar.SECOND, changeSecond);
			bean.setOpenTime(calendar.getTime());

		} else {
			// 初始 服务启动时如果有未开奖的记录 则更新为异常记录
			betService.updateGameIssueError();
			// 获取当前类型房间的初始期号
			bean = getInitExpect(bean);
			// 缓存上期开奖时间，如果下面的没获取到需要使用缓存的进行判断
		}

		Date lastExpectTime = bean.getOpenTime();

		blinker = Thread.currentThread();
		Thread thisThread = Thread.currentThread();
		while (blinker == thisThread) {

			// 没有维护时才检测，在维护交给下面else里检测
			if (!bean.isDown()) {
				// 初始检测是否维护 不是维护时间则继续运行，否则进入睡眠等待
				bean = isDownTime(roomTime, bean);
			}

			// 没有运行，在维护，且维护时间大于0
			if (bean.isDown() && bean.getDownTime() > 0) {
				try {
					// 获取正在运行房间
					List<GameRoom> roomList = betService.queryRoomList(roomType);
					Map<String, String> map = new HashMap<String, String>();
					map.put("expect", bean.getExpect());
					map.put("status", "-2");
					map.put("content", "系统维护,大约" + (bean.getDownTime() / 60) + "分钟");
					map.put("time", String.valueOf((bean.getDownTime() / 60)));
					PushMsgThread pushMsgThread = new PushMsgThread(roomList, JSONObject.toJSONString(map));
					taskExecutor.execute(pushMsgThread);
					log.info("游戏类型：{}， 停机维护时间准备 {} 分睡眠。。。", bean.getBetType(), (bean.getDownTime() / 60));

					Thread.sleep(bean.getDownTime() * 1000);
					bean = isDownTime(roomTime, bean);

				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

			// 如果没有在维护则运行状态。
			if (!bean.isDown()) {

				String tmpExpect = bean.getExpect();
				bean.setExpect(BetUtils.getNextExpect(tmpExpect));

				log.info("游戏类型：{} 正在进行 {} 期，准备获取开奖时间", bean.getBetType(), bean.getExpect());

				// 获取下一期 下一期时间
				try {
					bean = getExpectTime(bean);
					// 如果获取下一期号失败，则使用缓存开奖时间
					if (bean.isApiDown()) {
						log.error("游戏类型：{}，获取 {} 期开奖时间，API已经异常,使用原有时间 {} 加上游戏时间：{}",
								bean.getBetType(), bean.getExpect(), bean.getOpenTime(), bean.getGameTime());
						Date thisExpectTime = BetUtils.addSeconds(lastExpectTime, bean.getGameTime());
						bean.setOpenTime(thisExpectTime);

					}
				} catch (Exception e) {
					// 如果获取下一期号失败，则使用缓存开奖时间
					log.error("游戏类型：{}，获取下期开奖时间异常，异常：{}，使用原有时间：{}， 加上游戏时间：{}",
							bean.getBetType(),  e.getMessage(), bean.getOpenTime(), bean.getGameTime());
					Date thisExpectTime = BetUtils.addSeconds(lastExpectTime, bean.getGameTime());
					bean.setOpenTime(thisExpectTime);
				}
				// 滚动上期时间
				lastExpectTime = bean.getOpenTime();

				log.info("期号：{}，预计开奖时间：{}", bean.getOpenTime(), bean.getExpect());

				// 获取正在运行房间
				List<GameRoom> roomList = betService.queryRoomList(roomType);
				log.info("房间类型：{}，房间数量：{}", roomType.getRoomLotteryTicketType(), roomList.size());
				if (roomList != null && roomList.size() > 0) {
					GameRoom tmpRoom = roomList.get(0);
					// 生成推送游戏期号
					SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
					String currentIssue = sdf.format(new Date());
					currentIssue = currentIssue + "_" + roomType.getRoomLotteryTicketType();

					// 插入数据库时间
					long now = System.currentTimeMillis();
					long tmpSleepTime = bean.getOpenTime().getTime() - now;
					if (tmpSleepTime < 0) {
						// 如果时间小于0，利用continue跳转到下一期
						log.info("开奖时间：{}，当前时间：{}，如果时间小于0，利用continue跳转到下一期", bean.getOpenTime(), DateUtils.getNowDateString());
						continue;
					} else {

						GameIssue gameIssue = new GameIssue();
						gameIssue.setIssueApi(bean.getExpect());
						gameIssue.setStatus(GameConstants.GameIssueStatus_Running);
						List<GameIssue> list = betService.queryGameIssue(gameIssue);
						if (list.size() <= 0) {
							betService.addBatchGameIssue(roomList, Integer.parseInt(roomType.getRoomLotteryTicketType()), currentIssue, bean.getExpect(), bean.getOpenTime());
						}

						// 推送已生成期号的消息
						Map<String, String> msgMap = new HashMap<String, String>();
						msgMap.put("expect", bean.getExpect());
						msgMap.put("status", "0");
						msgMap.put("content", "已生成期号，准备下注");
						PushMsgThread pushMsgThread = new PushMsgThread(roomList, JSONObject.toJSONString(msgMap));
						taskExecutor.execute(pushMsgThread);
					}

					try {
						tmpSleepTime = tmpSleepTime - (tmpRoom.getDuration() * 1000);

						log.info("游戏类型：{},等待用户下注，{}秒后封盘", bean.getBetType(), (tmpSleepTime / 1000));
						if (tmpSleepTime > 0) {
							/**
							 * 机器人发言
							 */
							for (GameRoom room : roomList) {
								robotSpeak.execute(room.getRoomnumber(), now, tmpSleepTime, bean.getExpect());
							}
							Thread.sleep(tmpSleepTime);
						}
					} catch (InterruptedException e) {
						e.printStackTrace();
					}

					// 封盘
					Map<String, String> msgMap = new HashMap<String, String>();
					msgMap = new HashMap<String, String>();
					msgMap.put("expect", bean.getExpect());
					msgMap.put("status", "1");
					msgMap.put("content", "封盘");
					PushMsgThread pushMsgThread = new PushMsgThread(roomList, JSONObject.toJSONString(msgMap));
					taskExecutor.execute(pushMsgThread);
					betService.updateGameIssueClose(GameConstants.GameIssueStatus_Close, currentIssue);

					try {
						log.info("游戏类型：{}，已封盘，等待开奖， {} 秒后生成下一期, {} 秒后开奖",
								bean.getBetType(), tmpRoom.getLotteryTime(), tmpRoom.getLotteryTime());
						Thread.sleep(tmpRoom.getLotteryTime() * 1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}

					PushResultBean pushResultBean = new PushResultBean(taskExecutor, bean.getExpect(),
							bean.getBetType(), currentIssue, betService, roomList);
					taskExecutor.execute(pushResultBean);

				} else {
					while (roomList == null || roomList.size() == 0) {
						try {
							log.info("游戏类型：{},60秒等待是否有数据。。。", bean.getBetType());
							Thread.sleep(60000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						roomList = betService.queryRoomList(roomType);
					}
					bean = getInitExpect(bean);
				}
			} else {
				// 查看维护时间是否已过
				bean = isDownTime(roomTime, bean);
				log.info("游戏类型" + bean.getBetType() + "检查维护时间是否已过，" + bean.isDown() + "");
				// 如果维护时间已经过了，
				// 还要检查一遍，api获取的期号是否和维护前相同，如果相同则表示api还没恢复可能是维护延时
				if (!bean.isDown()) {
					// 尝试获取初始期号
					RoomBean tmpBean = new RoomBean();
					tmpBean.setBetType(bean.getBetType());
					tmpBean.setGameTime(bean.getGameTime());
					tmpBean.setDuration(bean.getDuration());
					tmpBean = getInitExpect(tmpBean);

					log.info("游戏类型" + bean.getBetType() + "维护时间已过，" + bean.isDown() + ",额外检查期号是否正常,缓存期号:"
							+ bean.getExpect() + ",Api最新期号:" + tmpBean.getExpect());
					// 如果相同期号,或比较小的旗号，表示维护还未结束
					if (tmpBean.getExpect().compareTo(bean.getExpect()) > 0) {
						bean = getInitExpect(bean);
					} else {
						bean.setDown(true);
						log.info("游戏类型：{}，维护尚未结束。。。10秒后重新验证。", bean.getBetType());
					}
				}
				try {
					log.info("游戏类型：{} 正在维护。。。10秒后检查维护时间是否已过。。。", bean.getBetType());
					Thread.sleep(10000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 定义roomBean存储房间运行状态
	 *
	 * @author zhuiyv
	 * @date 2018-07-17
	 */
	public static class RoomBean {
		// 类型
		private String betType;

		// 游戏时间
		private int gameTime;
		// 当前期号
		private String expect;
		// 当前开奖时间
		private Date openTime;

		// 维护时间 单位秒
		private int downTime = 0;
		// 是否在维护
		private boolean down;

		// api是否停掉
		private boolean apiDown;

		private int duration;

		// 传递给DataUtils存储apilog使用
		private BetService betService;

		public String getExpect() {
			return expect;
		}

		public void setExpect(String expect) {
			this.expect = expect;
		}

		public Date getOpenTime() {
			return openTime;
		}

		public void setOpenTime(Date openTime) {
			this.openTime = openTime;
		}

		public int getDownTime() {
			return downTime;
		}

		public void setDownTime(int downTime) {
			this.downTime = downTime;
		}

		public boolean isDown() {
			return down;
		}

		public void setDown(boolean down) {
			this.down = down;
		}

		public String getBetType() {
			return betType;
		}

		public void setBetType(String betType) {
			this.betType = betType;
		}

		public int getGameTime() {
			return gameTime;
		}

		public void setGameTime(int gameTime) {
			this.gameTime = gameTime;
		}

		public int getDuration() {
			return duration;
		}

		public void setDuration(int duration) {
			this.duration = duration;
		}

		public boolean isApiDown() {
			return apiDown;
		}

		public void setApiDown(boolean apiDown) {
			this.apiDown = apiDown;
		}

		public BetService getBetService() {
			return betService;
		}

		public void setBetService(BetService betService) {
			this.betService = betService;
		}
	}

	/**
	 * 是否停机维护
	 *
	 * @return
	 */
	public RoomBean isDownTime(GameRoom roomTime, RoomBean bean) {
		if (roomTime == null) {
			bean.setDownTime(60);
			bean.setDown(true);
		} else {
			Date now = new Date();
			SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
			String nowStr = sdf.format(now);
			try {
				int isDown = BetUtils.isShutDown(nowStr, roomTime.getMaintenance_start_time(),
						roomTime.getMaintenance_end_time());
				if (isDown != -1) {
					bean.setDownTime(isDown);
					bean.setDown(true);
				} else {
					bean.setDownTime(0);
					bean.setDown(false);
				}
			} catch (ParseException e) {
				e.printStackTrace();
			}
		}
		return bean;
	}

	/**
	 * 获取游戏期次，和开奖时间
	 *
	 * @param betService
	 * @return
	 */
	private RoomBean getExpectTime(RoomBean bean) {
		// 默认设置API已经down 除非下面获取到下期期号和时间
		bean.setApiDown(true);

		ResultEntity re = null;
		// 循环尝试2次 获取接口期号
		for (int i = 0; i < 2; i++) {
			re = DataUtil.getIssueApi(bean);
			if (!ResultCode.SUCCESS.equals(re.getStatusCode())) {
				log.error("游戏类型:" + bean.getBetType() + ",获取异常：" + re.getResultInfo());
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} else {
				break;
			}
		}

		Object retObj = re.getResultInfo();
		if (retObj instanceof Map) {
			@SuppressWarnings("unchecked")
			Map<String, Object> map = (Map<String, Object>) retObj;
			// openTime已改为下期时间
			Date tmpOpenTime = (Date) map.get("openTime");
			bean.setOpenTime(tmpOpenTime);

			Integer sleepTime = (Integer) map.get("sleepTime");
			// 补偿睡眠时间，当期剩余时间不满30秒，睡眠这段时间，等下期开盘。开下下期的下注
			if (sleepTime != 0) {
				try {
					log.info("游戏类型:" + bean.getBetType() + ",进行" + sleepTime + "秒补充睡眠时间。。。");
					Thread.sleep(sleepTime * 1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			bean.setApiDown(false);
		}
		log.info("API状态：" + bean.isApiDown());
		return bean;
	}

	/**
	 * 获取初始游戏期号
	 *
	 * @param betService
	 * @return
	 */
	private RoomBean getInitExpect(RoomBean bean) {
		// 默认设置认为API已经down了， 除非下面获取到下期期号和时间
		bean.setApiDown(true);

		ResultEntity re = null;
		// 循环尝试2次 获取接口期号
		for (int i = 0; i < 2; i++) {
			re = DataUtil.getIssueApi(bean);
			if (!ResultCode.SUCCESS.equals(re.getStatusCode())) {
				log.error("游戏类型:" + bean.getBetType() + ",获取异常：" + re.getResultInfo());
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} else {
				break;
			}
		}

		Object retObj = re.getResultInfo();
		if (retObj instanceof Map) {
			@SuppressWarnings("unchecked")
			Map<String, Object> map = (Map<String, Object>) retObj;
			// 获取期号
			bean.setExpect((String) map.get("expect"));
			bean.setOpenTime((Date) map.get("openTime"));
			bean.setApiDown(false);
		}
		log.info("API状态：{}, expect: {}, openTime：{}", bean.isApiDown(), bean.getExpect(), bean.getOpenTime());
		return bean;
	}

}
